#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <iostream>
#include "american.h"
#include "maximum.h"
#include "MatrixRegression.h"
#define steps 200

American::American(){
	days_in_year = 365;
	underPrice = 100;
	vol = .2;
	strike = 100;
	strike2 = 100;
	strike3 = 100;
	interest = .06;
	days = 365;
	putcall = 1; 
	current_value=0;
	dividend_rate=0;
	delta=0;
	theta=0;
	maturity = days/days_in_year;}
	void American::compute() {
	double dt;
	double u;
	double d;
	double p;
	double disc;
	int i;
	int j;
	double temp;
	double temp1;
	double temp2;
	double St[steps][steps]; //first "steps" = period no., 2nd steps = values per period
	double C[steps][steps];

	//precompute constants

	dt = (days/days_in_year)/steps;
	u = exp(((interest-dividend_rate-pow(vol,2))*dt)+vol*sqrt(dt));
	//	printf("u is %10.5f\n", u);
	d = exp(((interest-dividend_rate-pow(vol,2))*dt)-vol*sqrt(dt));
	//	printf("d is %10.5f\n", d);
	p = (exp((interest-dividend_rate)*dt)-d)/(u-d);
//	printf("p is %10.5f\n", p);
	disc= exp(-interest*dt);
	//initialize asset prices

	for (i = 0; i < steps; i++)
	{
		St[i][0] = underPrice*pow(d,i);
		// printf("St[i][j] is %10.5f, %10i, 0\n", St[i][0],i);
		for ( j = 1; j <= i; j++ )
		{
		St[i][j] = St[i][j-1]*(u/d);
		// printf("St[i][j] is %10.5f, %10i, %10i\n", St[i][j],i,j);
		}
	}
	if (putcall == 2)
	{
		for ( j = 0; j <= steps-1; j++ )

		{
		 C[steps-1][j] = maximum (strike-St[steps-1][j],0);
		}
		//step back through the tree applying early exercise condition
		for ( i = steps-2; i >= 0; i--)	
		{
			for ( j = 0; j <= i; j++ )
			{
			// printf("i,j is %10i,%10i\n", i,j);
			temp = p*C[i+1][j+1];
			temp1 = (1-p)*C[i+1][j];
			C[i][j] = (temp + temp1)*disc;
			//printf("C[i][j] is %10.5f\n", C[i][j]);
			//printf("St[i][j] is %10.5f\n", St[i][j]);
			 C[i][j] = maximum(C[i][j],strike-St[i][j]);
			}
		}
		current_value = C[0][0];
		temp = C[1][1]-C[1][0];
		temp1 = St[1][1]-St[1][0];
		delta = temp/temp1;
		temp = C[2][1]-C[0][0];
		temp1 = 2*dt;
		theta = temp/temp1;
		temp = (C[2][2]-C[2][1])/(St[2][2]-St[0][0]);
		temp1 = (C[2][1]-C[2][0])/(St[0][0]-St[2][0]);
		temp2 = .5*(St[2][2]-St[2][0]);
		gamma = (temp-temp1)/temp2;
	}
	else if(putcall == 1)
	{
		for ( j = 0; j <= steps-1; j++ )

		{
		C[steps-1][j] = maximum (St[steps-1][j]-strike,0);
		}
		//step back through the tree applying early exercise condition
		for ( i = steps-2; i >= 0; i--)	
		{
			for ( j = 0; j <= i; j++ )
			{
			// printf("i,j is %10i,%10i\n", i,j);
			temp = p*C[i+1][j+1];
			temp1 = (1-p)*C[i+1][j];
			C[i][j] = (temp + temp1)*disc;
			//printf("C[i][j] is %10.5f\n", C[i][j]);
			//printf("St[i][j] is %10.5f\n", St[i][j]);
			C[i][j] = maximum(C[i][j],St[i][j]-strike);
			}
		}
		current_value = C[0][0];
		temp = C[1][1]-C[1][0];
		temp1 = St[1][1]-St[1][0];
		delta = temp/temp1;
		temp = C[2][1]-C[0][0];
		temp1 = 2*dt;
		theta = temp/temp1;
		temp = (C[2][2]-C[2][1])/(St[2][2]-St[0][0]);
		temp1 = (C[2][1]-C[2][0])/(St[0][0]-St[2][0]);
		temp2 = .5*(St[2][2]-St[2][0]);
		gamma = (temp-temp1)/temp2;
	}
	else 
	{
	printf("Error.  Put-call designation is incorrect.");
	}
	}
	void American::setUnderlyingValue(double u1)
	{
		underPrice = u1;
	}
	void American::setVol (double vly1)
	{
		vol = vly1;
	}
	void American::setStrike(double sk)
	{
		strike = sk;
	}
	void American::setInterest(double i)
	{
		interest = i;
	}
	void American::setTimeToMaturity(double Ttt)
	{
		days = Ttt;
	}
	void American::setPutCallFlag(int p_c)
	{
		putcall = p_c;
	}
	void American::setDividendRate(double div)
	{
		dividend_rate = div;
	}
	void American::setStrike2(double sk_2)
	{
		strike2 = sk_2;
	}
	void American::setStrike3(double sk_3)
	{
		strike3 = sk_3;
	}
	void American::printInputs()
	{
		if(putcall == 1)
		{
			printf("The option is a call\n");
		}
		else if(putcall == 2)
		{
 			printf("The option is a put.\n");
		}
		else
		{
		 		printf("Put-call designation has not been properly assigned.\n");
		}
		printf("Underlying price is %5.5f.\n", underPrice);
		printf("Strike is %5.5f.\n", strike);
		printf("Volatility is %5.5f.\n", vol);
		printf("Interest rate is %5.5f.\n", interest);
		printf("Days to maturity are %5.5f.\n", days);
		printf("No. of steps applied was %4i.\n", steps);
		printf("Continuous dividend yield is %5.5f.\n", dividend_rate); 
	}

	double American::getOptPrice()
	{
	return current_value;
	}
	double American::getDelta()
	{
		return (delta);
	}
	double American::getGamma()
	{
		return (gamma);
	}
	double American::getTheta()
	{
		return (theta);
	}
	double American::getVega()
	{
		double original_value;
		double original_vol = vol;
		if (current_value == 0)
		{
		compute();
		}
		original_value = current_value;
		vol = vol+vol*.001;
		compute();
		vol = original_vol;
		return (current_value - original_value)/(vol*.001);
	}
	void American::setBetas(double B0, double B1, double B2)
	{
		beta0 = B0;
		beta1 = B1;
		beta2 = B2;
	}
	double American::LongCallStraddle()
	{
		double call_value;
		double call_delta;
		double call_gamma;
		double call_theta;
		double call_vega;
		double put_value;
		double put_delta;
		double put_gamma;
		double put_theta;
		double put_vega;
		double net_value;
		double net_delta;
		double net_gamma;
		double net_theta;
		double net_vega;			
		setPutCallFlag(1);
		compute();
		call_value = getOptPrice();
		call_delta = getDelta();
		call_gamma = getGamma();
		call_theta = getTheta();
		call_vega = getVega();
		setPutCallFlag(2);
		compute();
		put_value = getOptPrice();
		put_delta = getDelta();
		put_gamma = getGamma();
		put_theta = getTheta();
		put_vega = getVega();
		net_value = call_value + put_value; 
		net_delta = call_delta + put_delta;
		net_gamma = call_gamma + put_gamma;
		net_theta = call_theta + put_theta;
		net_vega = call_vega + put_vega;
		printf("Value of long straddle is %5.5f.\n", net_value);
		printf("Value from call is %5.5f.  Value from put is %5.5f.\n", call_value, put_value);
		printf("Net Delta is %5.5f.  Delta from call is %5.5f.  Delta from put is %5.5f.\n", net_delta, call_delta,put_delta); 
		printf("Net Gamma is %5.5f.  Gamma from call is %5.5f.  Gamma from put is %5.5f.\n", net_gamma,call_gamma,put_gamma); 
		printf("Net Theta is %5.5f. Theta from call is %5.5f. Theta from put is %5.5f.\n", net_theta,call_theta,put_theta);
		printf("Net Vega is %5.5f.  Vega from call is %5.5f.  Vega from put is %5.5f.\n", net_vega,call_vega,put_vega);
		return net_value;   	
	}
	double American::LongCallSpread()
	{
		double long_call_value;
		double long_call_delta;
		double long_call_gamma;
		double long_call_theta;
		double long_call_vega;
		double short_call_value;
		double short_call_delta;
		double short_call_gamma;
		double short_call_theta;
		double short_call_vega;
		double net_value;
		double net_delta;
		double net_gamma;
		double net_theta;
		double net_vega;
		double original_strike = strike;			
		setPutCallFlag(1);
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2);
		compute();
		long_call_value = getOptPrice();
		long_call_delta = getDelta();
		long_call_gamma = getGamma();
		long_call_theta = getTheta();
		long_call_vega = getVega();

		if (strike >= strike2)
		{
		strike2 = 1.1*strike;
		printf("Strike of short call ('strike2') must be higher than that of long call.\n");
		printf("Strike2 has been reset to %5.5f.\n",strike2);
		}
		strike = strike2;
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2);
		compute();
		short_call_value = -getOptPrice();
		short_call_delta = -getDelta();
		short_call_gamma = -getGamma();
		short_call_theta = -getTheta();
		short_call_vega = -getVega();
		strike = original_strike;
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2); // reset vol
		net_value = long_call_value + short_call_value; 
		net_delta = long_call_delta + short_call_delta;
		net_gamma = long_call_gamma + short_call_gamma;
		net_theta = long_call_theta + short_call_theta;
		net_vega = long_call_vega + short_call_vega;
		printf("Value of long call spread is %5.5f.\n", net_value);
		printf("Value from +call is %5.5f.  Value from -call is %5.5f.\n", long_call_value, short_call_value);
		printf("Net Delta is %5.5f. +call delta is %5.5f. -call delta is %5.5f.\n", net_delta, long_call_delta,short_call_delta); 
		printf("Net Gamma is %5.5f.  +call gamma is %5.5f. -call gamma is %5.5f.\n", net_gamma,long_call_gamma,short_call_gamma); 
		printf("Net Theta is %5.5f. +call theta is %5.5f. -call theta from put is %5.5f.\n", net_theta,long_call_theta,short_call_theta);
		printf("Net Vega is %5.5f.  +call vega is %5.5f.  -call vega is %5.5f.\n", net_vega,long_call_vega,short_call_vega);
		return net_value;   	
	}

	double American::LongButterflySpread()
	{
		double long_call_value_low_sk;
		double long_call_delta_low_sk;
		double long_call_gamma_low_sk;
		double long_call_theta_low_sk;
		double long_call_vega_low_sk;
		double long_call_value_hi_sk;
		double long_call_delta_hi_sk;
		double long_call_gamma_hi_sk;
		double long_call_theta_hi_sk;
		double long_call_vega_hi_sk;
		double short_call_value;
		double short_call_delta;
		double short_call_gamma;
		double short_call_theta;
		double short_call_vega;
		double net_value;
		double net_delta;
		double net_gamma;
		double net_theta;
		double net_vega;
		double original_strike = strike;
			
		setPutCallFlag(1);
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2);
		compute();
		long_call_value_low_sk = getOptPrice();
		long_call_delta_low_sk = getDelta();
		long_call_gamma_low_sk = getGamma();
		long_call_theta_low_sk = getTheta();
		long_call_vega_low_sk = getVega();

		if (strike >= strike2)
		{
		strike2 = 1.1*strike;
		printf("Strike of short call ('strike2') must be higher than that of long call.\n");
		printf("Strike2 has been reset to %5.5f.\n",strike2);
		}
		if (strike2 >= strike3)
		{
		strike3 = 1.1*strike2;
		printf("Parameter 'strike3' must be greater than parameters 'strike' and strike2'.\n");
		printf("Strike3 has been reset to %5.5f.\n",strike3);
		}

		strike = strike2;
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2);
		compute();
		short_call_value = -2*getOptPrice();
		short_call_delta = -2*getDelta();
		short_call_gamma = -2*getGamma();
		short_call_theta = -2*getTheta();
		short_call_vega = -2*getVega();

		strike = strike3;
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2);
		compute();
		long_call_value_hi_sk = getOptPrice();
		long_call_delta_hi_sk = getDelta();
		long_call_gamma_hi_sk = getGamma();
		long_call_theta_hi_sk = getTheta();
		long_call_vega_hi_sk = getVega();

		strike = original_strike;
		vol = getVolatility(strike,underPrice,maturity,beta0,beta1,beta2);

		net_value = long_call_value_low_sk + short_call_value + long_call_value_hi_sk; 
		net_delta = long_call_delta_low_sk + short_call_delta + long_call_delta_hi_sk; 
		net_gamma = long_call_gamma_low_sk + short_call_gamma + long_call_gamma_hi_sk;
		net_theta = long_call_theta_low_sk + short_call_theta + long_call_theta_hi_sk;
		net_vega = long_call_vega_low_sk + short_call_vega + long_call_vega_hi_sk;

		printf("Value of long butterfly spread is %5.5f.\n", net_value);
		printf("Value from long call with low sk is %5.5f. Value from long call with high sk is %5.5f.\n", long_call_value_low_sk, long_call_value_hi_sk);
		printf("Value from short calls is %5.5f.\n", short_call_value);	
		printf("Net delta is %5.5f.\n",net_delta);  
	      printf("+call delta with low sk is %5.5f. +call delta with high sk is %5.5f.\n",long_call_value_low_sk,long_call_delta_low_sk);
		printf("Delta from short calls is %5.5f.\n", short_call_delta); 
		printf("Net gamma is %5.5f.\n", net_gamma);
		printf("+call gamma with high sk is %5.5f. +call gamma with low sk is %5.5f.\n",long_call_gamma_low_sk,long_call_gamma_hi_sk); 
   		printf("Gamma from short calls is %5.5f.\n", short_call_gamma);
		printf("Net theta is %5.5f.\n", net_theta);
		printf("+call theta with high sk is %5.5f. +call theta with low sk is %5.5f.\n",long_call_theta_low_sk,long_call_theta_hi_sk); 
   		printf("Theta from short calls is %5.5f.\n", short_call_theta);
		printf("Net vega is %5.5f.\n", net_vega);
		printf("+call vega with high sk is %5.5f. +call vega with low sk is %5.5f.\n",long_call_vega_low_sk,long_call_vega_hi_sk); 
   		printf("Vega from short calls is %5.5f.\n", short_call_vega);   	
	}
